﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.Lexical.Coding;
using Kokomo.Lexical.Expressions;

namespace Kokomo.PeInspector.Reflection.Disassembly.Flow
{
	class ExceptionFlowAnalyzer : FlowAnalyzer
	{
		public ExceptionFlowAnalyzer(
			FlowAnalyzer parentAnalyzer,
			int blockStartOffset,
			int blockEndOffset,
			int tryLength,
			IEnumerable<ExceptionHandlingClause> clauses
			)
			: base(parentAnalyzer, blockStartOffset, blockEndOffset, blockStartOffset, blockStartOffset + tryLength)
		{
			//MsilTryFlowHandler tryFlow = new MsilTryFlowHandler(
			//    this,
			//    blockStartOffset,
			//    blockStartOffset + tryLength
			//    );
			//this.TryFlow = tryFlow;

			List<ExceptionHandlerFlowAnalyzer> catchFlows = new List<ExceptionHandlerFlowAnalyzer>();
			foreach (var clause in clauses)
			{
				bool isFinally = (clause.Flags == ExceptionHandlingClauseOptions.Finally);
				Type catchType = (clause.Flags == ExceptionHandlingClauseOptions.Clause)
					? clause.CatchType
					: null
					;
				ExceptionHandlerFlowAnalyzer catchFlow = new ExceptionHandlerFlowAnalyzer(
					this,
					clause.HandlerOffset,
					clause.HandlerOffset + clause.HandlerLength,
					catchType
					);

				catchFlows.Add(catchFlow);
				if (isFinally)
				{
					this.FinallyFlow = catchFlow;
				}
			}
			this.HandlerFlows = catchFlows;

			this.TryBlock = new TryBlock();
			this.ActiveStatementBlock = this.TryBlock.TryStatements;
		}

		public TryBlock TryBlock { get; private set; }
		//public MsilTryFlowHandler TryFlow { get; private set; }
		public IEnumerable<ExceptionHandlerFlowAnalyzer> HandlerFlows { get; private set; }
		public ExceptionHandlerFlowAnalyzer FinallyFlow { get; private set; }
		//public IEnumerable<MsilCatchFlow> HandlerFlows { get; private set; }

		protected override void AnalyzeLocalBranch(MsilBranch branch)
		{
			if (!this.IsBranchToSubsequentBlock(branch))
			{
				base.AnalyzeLocalBranch(branch);
			}
			else
			{
				// This is the branch that ends the block

				this.Context.Advance();
			}
		}

		protected override void OnExitBody()
		{
			base.OnExitBody();

			foreach (var catchFlow in this.HandlerFlows)
			{
				this.AnalyzeInnerBlock(catchFlow);
				VariableDeclarationStatement statement = catchFlow.CatchStatements.FirstOrDefault() as VariableDeclarationStatement;
				if ((statement != null) && (statement.Initializer is ExceptionExpression))
				{
					catchFlow.CatchStatements.Remove(statement);
				}

				if (catchFlow != this.FinallyFlow)
				{
					CatchBlock catchBlock = new CatchBlock(catchFlow.ExceptionVariable);
					catchBlock.Statements.AddRange(catchFlow.CatchStatements);
					this.TryBlock.CatchBlocks.Add(catchBlock);
				}
				else
				{
					this.TryBlock.FinallyStatements.AddRange(this.FinallyFlow.CatchStatements);
				}
			}
		}
	}
}
